/**
 * @file dht11.c
 * @brief DHT11传感器驱动实现
 * 
 * 实现DHT11温湿度传感器的数据读取功能。
 */

#include "dht11.h"
#include "user_config.h"
#include "log_utils.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"

static const char *TAG = "dht11";

/**
 * @brief DHT11引脚输出模式设置
 * 
 * @param level 电平状态
 */
static void dht11_pin_output(uint32_t level)
{
    gpio_set_level(DHT11_GPIO, level);
}

/**
 * @brief DHT11引脚输入模式设置
 */
static void dht11_pin_input(void)
{
    gpio_set_direction(DHT11_GPIO, GPIO_MODE_INPUT);
}

/**
 * @brief 读取DHT11引脚电平
 * 
 * @return int 电平状态
 */
static int dht11_pin_read(void)
{
    return gpio_get_level(DHT11_GPIO);
}

/**
 * @brief DHT11引脚初始化
 */
static void dht11_pin_init(void)
{
    gpio_reset_pin(DHT11_GPIO);
    gpio_set_direction(DHT11_GPIO, GPIO_MODE_INPUT_OUTPUT_OD);
    gpio_set_level(DHT11_GPIO, 1);
}

/**
 * @brief 延迟微秒
 * 
 * @param us 微秒数
 */
static void dht11_delay_us(uint32_t us)
{
    esp_rom_delay_us(us);
}

/**
 * @brief 等待DHT11引脚电平变化
 * 
 * @param level 期望的电平状态
 * @param timeout_us 超时时间（微秒）
 * @return int 实际等待时间（微秒），负值表示超时
 */
static int dht11_wait_for_pin_state(int level, uint32_t timeout_us)
{
    int64_t start_time = esp_timer_get_time();
    int64_t current_time;
    
    do {
        if (dht11_pin_read() == level) {
            current_time = esp_timer_get_time();
            return (int)(current_time - start_time);
        }
        dht11_delay_us(1);
        current_time = esp_timer_get_time();
    } while ((current_time - start_time) < timeout_us);
    
    return -1; // 超时
}

/**
 * @brief 读取DHT11传感器数据
 * 
 * @param data 存储读取数据的结构体指针
 * @return esp_err_t ESP_OK表示成功，其他表示失败
 */
esp_err_t dht11_read_data(dht11_data_t *data)
{
    if (data == NULL) {
        ESP_LOGE(TAG, "无效的数据指针");
        return ESP_ERR_INVALID_ARG;
    }
    
    uint8_t bits[5] = {0}; // 40位数据存储
    uint16_t timeout = 0;
    
    // 初始化引脚
    dht11_pin_init();
    
    // 主机发送开始信号
    dht11_pin_output(0);
    dht11_delay_us(18000); // 至少18ms
    
    // 主机拉高并等待从机响应
    dht11_pin_output(1);
    dht11_delay_us(20);
    dht11_pin_input();
    
    // 等待从机拉低（响应信号）
    if (dht11_wait_for_pin_state(0, 40) < 0) {
        ESP_LOGE(TAG, "DHT11未响应（等待拉低超时）");
        return ESP_FAIL;
    }
    
    // 等待从机拉高（准备发送数据）
    if (dht11_wait_for_pin_state(1, 80) < 0) {
        ESP_LOGE(TAG, "DHT11响应错误（等待拉高超时）");
        return ESP_FAIL;
    }
    
    // 等待从机拉低（开始发送数据）
    if (dht11_wait_for_pin_state(0, 80) < 0) {
        ESP_LOGE(TAG, "DHT11数据开始错误（等待拉低超时）");
        return ESP_FAIL;
    }
    
    // 读取40位数据
    for (int i = 0; i < 40; i++) {
        // 等待拉高（数据位开始）
        if (dht11_wait_for_pin_state(1, 50) < 0) {
            ESP_LOGE(TAG, "读取数据位%d时超时（等待拉高）", i);
            return ESP_FAIL;
        }
        
        // 延迟30us后读取电平判断是0还是1
        dht11_delay_us(30);
        if (dht11_pin_read() == 1) {
            // 高电平表示1
            bits[i / 8] |= (1 << (7 - (i % 8)));
        }
        
        // 等待拉低（数据位结束）
        if (dht11_wait_for_pin_state(0, 50) < 0) {
            ESP_LOGE(TAG, "读取数据位%d时超时（等待拉低）", i);
            return ESP_FAIL;
        }
    }
    
    // 校验数据
    if (bits[4] != ((bits[0] + bits[1] + bits[2] + bits[3]) & 0xFF)) {
        ESP_LOGE(TAG, "数据校验失败");
        return ESP_FAIL;
    }
    
    // 解析数据
    data->humidity = (float)bits[0];
    data->humidity_decimal = (float)bits[1] / 10.0;
    data->temperature = (float)bits[2];
    data->temperature_decimal = (float)bits[3] / 10.0;
    
    // 合并整数和小数部分
    data->humidity += data->humidity_decimal;
    data->temperature += data->temperature_decimal;
    
    ESP_LOGD(TAG, "读取到温湿度数据: 温度=%.1f°C, 湿度=%.1f%%", 
             data->temperature, data->humidity);
    
    return ESP_OK;
}